package cn.analysys.test;

import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 * 一.流程文件部署
 *     1.定义流程文档
 *     2.部署方式
 *          a.文本方式(addString)部署
 *          b.classPath资源部署
 *          c.流式部署
 *              inputStream部署
 *              zipInputStream部署
 *          d.BpmnModel方式部署
 *     3.建议使用
 *          如果客户端需要开发一套流程设计器建立使用BpmnModel方式部署，因为该方式最灵活，可塑性更强
 *          如果项目中流程文档大部分内容是固定的，只有极少数属性需要在流程部署时动态从数据库中读取。建议使用
 *          addString方式部署
 *          如果一次性需要部署多个流程资源，建立使用zipInputStream部署
 *          classPath方式不建立在正式环境使用。
 *
 *
 *
 *
 *
 * 2.流程定义
 * <p>
 * 涉及表：
 * 创建一个用户(表：act_id_user)
 * engine.getIdentityService().saveUser()
 * 部署一份bpmn文件 (act_re_deployment、act_ge_bytearray、act_re_procdef)
 * engine.getRepositoryService().createDeployment().addClasspathResource("demo.bpmn").deploy();
 * 授权用户操作流程的权限 (act_ru_identitylink)
 * repositoryService.addCandidateStarterUser(processDefinition.getId(),user.getId());
 */
public class RepositoryTest2 {

    public static void main(String[] args) throws IOException {

        // 添加普通资源文件
//        addInputStream();

        // 添加压缩资源文件
//        addZipInputStream();

        // 添加自定义bpmn流程文件 (重要)
//        addBpmnModel();

        // 当bpmn文件内容错误的时候，可以通过  builder.disableSchemaValidation()强制提交
//        addErrorBpmn();

        //当bpmn文件流程错误的时候，可以通过 builder.disableBpmnValidation()强制提交
//        addErrorFlowBpmn();

        // 查询普通文件或bpmn文件
//        queryNormalDocument();

        // 查询bpmn文件
//        queryBpmnDocument();

        // 查询图片文件
//        queryImageDocument();

        // 删除部署资源
//        deleteDeployment();

        // 中止与激活流程定义
//        suspendPropessDefinition();

        // 授权用户操作流程的权限
        addCandidateStarterUser();

        // 不自动生成图片
//        test();
    }

    /**
     * 添加压缩资源文件
     */
    public static void addZipInputStream() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 创建部署文件builder
        DeploymentBuilder builder = repositoryService.createDeployment();

        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(RepositoryTest2.class.getResource("/").getPath() + "/datas/datas.zip"));

        builder.addZipInputStream(zipInputStream);
        builder.deploy();
    }

    /**
     * 添加普通资源文件
     */
    public static void addInputStream() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 创建部署文件builder
        DeploymentBuilder builder = repositoryService.createDeployment();

        FileInputStream fileInputStream = new FileInputStream(RepositoryTest2.class.getResource("/").getPath() + "/datas/datas.txt");

        builder.addInputStream("name", fileInputStream);
        builder.deploy();
    }

    /**
     * 添加自定义bpmn流程文件 （可以动态生成流程）
     */
    public static void addBpmnModel() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 创建部署文件builder
        DeploymentBuilder builder = repositoryService.createDeployment();

        // 创建bpmn模型对象
        BpmnModel bpmnModel = new BpmnModel();
        // 创建一个流程定义
        Process process = new Process();
        bpmnModel.addProcess(process);
        process.setId("myProcess");
        process.setName("My Process");

        // 开始事件
        StartEvent startEvent = new StartEvent();
        startEvent.setId("startEventId");
        process.addFlowElement(startEvent);

        // 用户任务
        UserTask userTask = new UserTask();
        userTask.setId("userTask");
        userTask.setName("User task");
        process.addFlowElement(userTask);

        // 结束事件
        EndEvent endEvent = new EndEvent();
        endEvent.setId("endEventId");
        process.addFlowElement(endEvent);

        // 添加流程顺序
        process.addFlowElement(new SequenceFlow("startEventId", "userTask"));
        process.addFlowElement(new SequenceFlow("userTask", "endEventId"));

        builder.addBpmnModel("bpmnModel", bpmnModel);
        builder.deploy();
    }

    /**
     * 当bpmn文件内容错误的时候，可以通过  builder.disableSchemaValidation()强制提交
     */
    public static void addErrorBpmn() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 创建部署文件builder
        DeploymentBuilder builder = repositoryService.createDeployment();

        builder.addClasspathResource("datas/error.bpmn");
        // 关闭格式验证
        builder.disableSchemaValidation();

        builder.deploy();
    }

    /**
     * 当bpmn文件流程错误的时候，可以通过 builder.disableBpmnValidation()强制提交
     */
    public static void addErrorFlowBpmn() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 创建部署文件builder
        DeploymentBuilder builder = repositoryService.createDeployment();

        builder.addClasspathResource("datas/error_flow.bpmn");
        // 关闭流程格式验证
        builder.disableBpmnValidation();

        builder.deploy();
    }

    /**
     * 查询普通文件或bpmn文件流程
     * <p>
     * select * from act_ge_bytearray where DEPLOYMENT_ID_ = 1 and NAME_ = 'datas/datas.txt'
     */
    public static void queryNormalDocument() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 部署一份txt文件
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("datas/datas.txt");
        // 流程部署完成后，会返回act_re_deployment表中对于数据
        Deployment deployment = builder.deploy();

        //流程查询
        // select * from act_ge_bytearray where DEPLOYMENT_ID_ = 1 and NAME_ = 'datas/datas.txt'
        InputStream inputStream = repositoryService.getResourceAsStream(deployment.getId(),
                "datas/datas.txt");

        // 通过流读取
        int count = inputStream.available();
        byte[] contents = new byte[count];
        inputStream.read(contents);
        System.out.println(new String(contents));
    }


    /**
     * 查询bpmn流程
     * <p>
     * bpmn的文件会在act_re_procdef表中产生一条记录
     * 可以通过查询act_re_procdef的记录来获取bpmn文件信息
     * select * from act_re_procdef where DEPLOYMENT_ID_  = 1
     */
    public static void queryBpmnDocument() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 部署一份bpmn文件
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("demo.bpmn");
        // 流程部署完成后，会返回act_re_deployment表中对于数据
        Deployment deployment = builder.deploy();

        // select * from act_re_procdef where DEPLOYMENT_ID_  = 1
        ProcessDefinitionQuery processQuery = repositoryService.createProcessDefinitionQuery();
        ProcessDefinition processDefinition = processQuery.deploymentId(deployment.getId()).singleResult();

        // 直接写sql查询
//        ProcessDefinition processDefinition = repositoryService.createNativeProcessDefinitionQuery()
//                .sql("select * from act_re_procdef where DEPLOYMENT_ID_ = #{deploymentId}")
//                .parameter("deploymentId",deployment.getId()).singleResult();

        //流程查询
        InputStream inputStream = repositoryService.getProcessModel(processDefinition.getId());

        // 通过流读取
        int count = inputStream.available();
        byte[] contents = new byte[count];
        inputStream.read(contents);
        System.out.println(new String(contents));
    }

    /**
     * 查询bpmn的流程图片
     * <p>
     * bpmn的文件会在act_re_procdef表中产生一条记录
     * 可以通过查询act_re_procdef的记录来获取bpmn文件信息
     * select * from act_re_procdef where DEPLOYMENT_ID_  = 1
     */
    public static void queryImageDocument() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();

        // 部署一份bpmn文件
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("demo.bpmn");
//        builder.addClasspathResource("demo.png");  // 不自己部署的话，demo.bpmn也会自动生成一个图片
        Deployment deployment = builder.deploy();  // 流程部署完成后，会返回act_re_deployment表中对于数据

        // select * from act_re_procdef where DEPLOYMENT_ID_  = 1
        ProcessDefinitionQuery processQuery = repositoryService.createProcessDefinitionQuery();
        ProcessDefinition processDefinition = processQuery.deploymentId(deployment.getId()).singleResult();

        // 查询流程 文件
        InputStream inputStream = repositoryService.getProcessDiagram(processDefinition.getId());

        // 将输入流转为图片
        BufferedImage image = ImageIO.read(inputStream);
        File file = new File(RepositoryTest2.class.getResource("/").getPath() + "datas/result.png");
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        ImageIO.write(image, "png", fileOutputStream);
        inputStream.close();
        fileOutputStream.close();
    }


    /**
     * deleteDeployment(String deploymentId);
     * 删除部署数据,不进行级联删除，这里所说的级联删除，是指与该部署相关的流程实例数据
     * <p>
     * deleteDeployment(String deploymentId,boolean cascade);
     * 删除部署数据,是否进行级联删除，由调用者进行决定。
     * 如果cascade为false,不进行级联删除
     * 如果cascade为true,进行级联删除
     * <p>
     * 不管删除部署数据时是否指定级联删除，部署的相关数据都会被删除，包括：
     * 身份数据（IdentityLink）、流程定义数据（ProcessDefinition）
     * 流程资源（Resource）、部署数据（Deployment）
     * <p>
     * 如果设置级联删除，则会删除流程实例数据（ProcessInstance），
     * 其中流程实例数据也包括流程任务（Task）与流程实例历史数据。
     */
    public static void deleteDeployment() throws FileNotFoundException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 部署bpmn文件
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("demo.bpmn");
        Deployment deployment = builder.deploy();

        // 创建测试数据：用来看会不会被删除
        // 创建流程定义数据
//        ProcessDefinitionQuery processQuery = repositoryService.createProcessDefinitionQuery();
//        ProcessDefinition processDefinition = processQuery.deploymentId(deployment.getId()).singleResult();
        // 创建流程实例数据
//        engine.getRuntimeService().startProcessInstanceById(processDefinition.getId());

        repositoryService.deleteDeployment(deployment.getId());
    }

    /**
     * 不自动生成流程图
     */
    public static void test() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();

        // 部署一份bpmn文件
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("demo.bpmn").addClasspathResource("datas/demo.png");
        Deployment deployment = builder.deploy();  // 流程部署完成后，会返回act_re_deployment表中对于数据
    }

    /**
     * 1.流程一旦被中止，不允许再次启动
     * 2.流程定义状态已经是中止，在调用中止方法，将会抛出异常，激活亦然。
     */
    public static void suspendPropessDefinition() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 部署一份bpmn文件
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("demo.bpmn").deploy();
        // 查询流程定义实体
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId()).singleResult();

        // 中止流程定义
        repositoryService.suspendProcessDefinitionById(processDefinition.getId());
        repositoryService.suspendProcessDefinitionByKey(processDefinition.getKey());

        // 激活流程定义
        repositoryService.activateProcessDefinitionById(processDefinition.getId());
        repositoryService.activateProcessDefinitionByKey(processDefinition.getKey());

        // 启动流程实例，如果流程定义已经被中止，会抛出异常
        RuntimeService runtimeService = engine.getRuntimeService();
        runtimeService.startProcessInstanceById(processDefinition.getId());
    }


    /**
     * 授权用户操作流程的权限
     */
    public static void addCandidateStarterUser() throws IOException {
        // 创建流程引擎
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 创建一个用户(表：act_id_user)
        IdentityService identityService = engine.getIdentityService();
        User user = identityService.newUser("101");
        user.setFirstName("jiangbang");
        identityService.saveUser(user);

        // 得到流程存储服务对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 部署一份bpmn文件 (act_re_deployment、act_ge_bytearray、act_re_procdef)
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("demo.bpmn").deploy();

        // 查询流程定义实体 (查询：act_re_procdef)
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId()).singleResult();

        // 授权用户操作流程的权限 (act_ru_identitylink)
        repositoryService.addCandidateStarterUser(processDefinition.getId(), user.getId());

        // 查询用户有权限启动的流程定义
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().startableByUser(user.getId()).list();
        for (ProcessDefinition process : processDefinitions) {
            System.out.println(process.getId());
        }
    }

}
